/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <inttypes.h>
#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "file_manager.h"
#include "pwm_audio.h"

static const char *TAG = "wav player";

typedef struct {
    // The "RIFF" chunk descriptor
    uint8_t ChunkID[4];
    int32_t ChunkSize;
    uint8_t Format[4];
    // The "fmt" sub-chunk
    uint8_t Subchunk1ID[4];
    int32_t Subchunk1Size;
    int16_t AudioFormat;
    int16_t NumChannels;
    int32_t SampleRate;
    int32_t ByteRate;
    int16_t BlockAlign;
    int16_t BitsPerSample;
    // The "data" sub-chunk
    uint8_t Subchunk2ID[4];
    int32_t Subchunk2Size;
} wav_header_t;

#ifdef CONFIG_STORAGE_SDCARD
static char **g_file_list = NULL;
static uint16_t g_file_num = 0;
#endif

#ifdef CONFIG_IDF_TARGET_ESP32
#define GPIO_AUDIO_OUTPUT_L 25
#define GPIO_AUDIO_OUTPUT_R 26
#elif defined CONFIG_IDF_TARGET_ESP32S2
#define GPIO_AUDIO_OUTPUT_L 1
#define GPIO_AUDIO_OUTPUT_R 2
#elif defined CONFIG_IDF_TARGET_ESP32S3
#define GPIO_AUDIO_OUTPUT_L 1
#define GPIO_AUDIO_OUTPUT_R 2
#elif defined CONFIG_IDF_TARGET_ESP32C3
#define GPIO_AUDIO_OUTPUT_L 3
#define GPIO_AUDIO_OUTPUT_R 2
#else
#define GPIO_AUDIO_OUTPUT_L 1
#define GPIO_AUDIO_OUTPUT_R 2
#endif

static esp_err_t play_wav(const char *filepath)
{
    FILE *fd = NULL;
    struct stat file_stat;

    if (stat(filepath, &file_stat) == -1) {
        ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "file stat info: %s (%ld bytes)...", filepath, file_stat.st_size);
    fd = fopen(filepath, "r");

    if (NULL == fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        return ESP_FAIL;
    }
    const size_t chunk_size = 4096;
    uint8_t *buffer = malloc(chunk_size);

    if (NULL == buffer) {
        ESP_LOGE(TAG, "audio data buffer malloc failed");
        fclose(fd);
        return ESP_FAIL;
    }

    /**
     * read head of WAV file
     */
    wav_header_t wav_head;
    int len = fread(&wav_head, 1, sizeof(wav_header_t), fd);
    if (len <= 0) {
        ESP_LOGE(TAG, "Read wav header failed");
        fclose(fd);
        return ESP_FAIL;
    }
    if (NULL == strstr((char *)wav_head.Subchunk1ID, "fmt") &&
            NULL == strstr((char *)wav_head.Subchunk2ID, "data")) {
        ESP_LOGE(TAG, "Header of wav format error");
        fclose(fd);
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "frame_rate= %"PRIi32", ch=%d, width=%d", wav_head.SampleRate, wav_head.NumChannels, wav_head.BitsPerSample);

    pwm_audio_set_param(wav_head.SampleRate, wav_head.BitsPerSample, wav_head.NumChannels);
    pwm_audio_start();

    /**
     * read wave data of WAV file
     */
    size_t write_num = 0;
    size_t cnt;

    do {
        /* Read file in chunks into the scratch buffer */
        len = fread(buffer, 1, chunk_size, fd);
        if (len <= 0) {
            break;
        }
        pwm_audio_write(buffer, len, &cnt, 1000 / portTICK_PERIOD_MS);

        write_num += len;
    } while (1);

    pwm_audio_stop();
    fclose(fd);
    ESP_LOGI(TAG, "File reading complete, total: %d bytes", write_num);
    return ESP_OK;
}

void app_main()
{
    esp_err_t ret;

    ESP_LOGI(TAG, "[APP] Startup..");
    ESP_LOGI(TAG, "[APP] Free memory: %"PRIu32" bytes", esp_get_free_heap_size());
    ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());

    pwm_audio_config_t pac = {
        .duty_resolution    = LEDC_TIMER_10_BIT,
        .gpio_num_left      = GPIO_AUDIO_OUTPUT_L,
        .ledc_channel_left  = LEDC_CHANNEL_0,
        .gpio_num_right     = GPIO_AUDIO_OUTPUT_R,
        .ledc_channel_right = LEDC_CHANNEL_1,
        .ledc_timer_sel     = LEDC_TIMER_0,
        .ringbuf_len        = 1024 * 8,
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
        .tg_num             = TIMER_GROUP_0,
        .timer_num          = TIMER_0,
#endif
    };
    pwm_audio_init(&pac);

#ifdef CONFIG_STORAGE_SDCARD
    ret = fm_sdcard_init();
    if (ESP_OK != ret) {
        ESP_LOGE(TAG, "sdcard initial failed, exit");
        return;
    }
    fm_print_dir(MOUNT_POINT, 2);
    fm_file_table_create(&g_file_list, &g_file_num, ".wav");
    for (size_t i = 0; i < g_file_num; i++) {
        ESP_LOGI(TAG, "have file [%d:%s]", i, g_file_list[i]);
    }
    if (0 == g_file_num) {
        ESP_LOGW(TAG, "Can't found any wav file in sdcard!");
        return;
    }

    for (size_t i = 0; i < g_file_num; i++) {
        ESP_LOGI(TAG, "Start to play [%d:%s]", i, g_file_list[i]);
        char path_buf[256] = {0};
        sprintf(path_buf, "%s/%s", MOUNT_POINT, g_file_list[i]);
        play_wav(path_buf);
    }
    fm_file_table_free(&g_file_list, g_file_num);

#else
    ret = fm_spiffs_init();
    if (ESP_OK != ret) {
        ESP_LOGE(TAG, "sdcard initial failed, exit");
        return;
    }
    play_wav(MOUNT_POINT "/sample.wav");
#endif

}
